﻿package com.zouloux.touch 
{
	import com.zouloux.core.base.DocumentBase;
	//import com.zouloux.core.debug.Debug;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TouchEvent;
	import flash.filters.DropShadowFilter;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	
	/**
	 * @author ZoulouX
	 */
	public class TouchEmulator
	{
		// L'ombre
		static protected var _shadow				:DropShadowFilter;
		
		// Emuler les points
		protected static var __multiTouchEmulated	:Boolean				= false;
		
		// La liste des points
		protected static var __touchPoints			:Array					= [];
		
		// Le point pour faire l'effet pinch/zoom
		protected static var __pinchPoint			:Point;
		
		// Le clip pour le pinch
		protected static var __pinchSprite			:Sprite;
		
		// En mode pinch zoom
		protected static var __pinchMode			:Boolean				= false;
		
		// L'id du premier point émulé
		public static const FIRST_TOUCH_ID			:uint					= 10;
		
		// L'id du second point émulé
		public static const SECOND_TOUCH_ID			:uint					= 11;
		
		// La taille émulée
		public static const DEFAULT_TOUCH_SIZE		:uint					= 40;
		
		// La cible pour cet émulateur
		protected var _target						:DisplayObject;
		
		
		static public function get multiTouchEmulated():Boolean { return __multiTouchEmulated; }
		
		/**
		 * Emuler plusieurs points
		 */
		static protected function emulateMultiTouch ():void
		{
			// Si l'émulateur multiTouch n'a pas été activé
			if (!__multiTouchEmulated)
			{
				//Debug.core(TouchEmulator, "MULTITOUCH EMULATOR ENABLED", arguments);
				
				// L'activer
				__multiTouchEmulated = true;
				
				// Ecouter la touche controle
				DocumentBase.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
				DocumentBase.stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			}
		}
		
		/**
		 * Touche enfoncée
		 * @param	e
		 */
		static protected function keyDownHandler (e:KeyboardEvent):void 
		{
			// Si c'est la touche control
			if (e.keyCode == Keyboard.CONTROL && !__pinchMode)
			{
				// On passe en pinchMode
				__pinchMode = true;
				
				// On récupère la position du curseur
				__pinchPoint = new Point(DocumentBase.stage.mouseX, DocumentBase.stage.mouseY);
				
				// Tracer la croix
				__pinchSprite = new Sprite();
				__pinchSprite.graphics.lineStyle(1, 0x000000);
				__pinchSprite.graphics.moveTo(-20, 0);
				__pinchSprite.graphics.lineTo(20, 0);
				__pinchSprite.graphics.moveTo(0, 20);
				__pinchSprite.graphics.lineTo(0, -20);
				__pinchSprite.mouseEnabled = false;
				__pinchSprite.hitArea = new Sprite();
				
				// La placer
				__pinchSprite.x = __pinchPoint.x;
				__pinchSprite.y = __pinchPoint.y;
				
				// L'ajouter
				DocumentBase.stage.addChild(__pinchSprite);
			}
		}
		
		/**
		 * Touche relevée
		 * @param	e
		 */
		static protected function keyUpHandler (e:KeyboardEvent):void 
		{
			// Si c'est la touche control
			if (e.keyCode == Keyboard.CONTROL)
			{
				// On passe en pinchMode
				__pinchMode = false;
				
				// Effacer le clip
				if (__pinchSprite != null && DocumentBase.stage.contains(__pinchSprite))
					DocumentBase.stage.removeChild(__pinchSprite);
			}
		}
		
		/**
		 * Afficher les points d'entrés
		 */
		public static function showTouchPoints ():void
		{
			//Debug.core(TouchEmulator, "TOUCH POINTS TRACKING ENABLED");
			
			// Créer l'ombre pour les points
			if (_shadow == null)
				_shadow = new DropShadowFilter(0, 90, 0x000000, .7, 12, 12, 1, 1, false);
			
			// Emuler le stage pour afficher les points même avec la souris
			emulate(DocumentBase.stage);
			
			// Ecouter les évènements sur le stage pour afficher les points
			DocumentBase.stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler);
			DocumentBase.stage.addEventListener(TouchEvent.TOUCH_END, touchEndHandler);
			DocumentBase.stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler);
		}
		
		/**
		 * Ne plus afficher les points
		 */
		public static function hideTouchPoints ():void
		{
			DocumentBase.stage.removeEventListener(TouchEvent.TOUCH_BEGIN, touchBeginHandler);
			DocumentBase.stage.removeEventListener(TouchEvent.TOUCH_END, touchEndHandler);
			DocumentBase.stage.removeEventListener(TouchEvent.TOUCH_MOVE, touchMoveHandler);
		}
		
		/**
		 * Lorsque un touch démarre
		 * @param	e
		 */
		static protected function touchBeginHandler (e:TouchEvent):void 
		{
			// Si c'est sur le stage uniquement (les autres étapes de l'event sont dégagées)
			if (e.target == DocumentBase.stage || Multitouch.inputMode == MultitouchInputMode.TOUCH_POINT)
			{
				// Masquer la souris
				Mouse.hide();
				
				// On fait le sprite
				var p:Sprite = new Sprite();
				
				// Ne surtout pas autoriser les intéractions à la souris
				p.mouseEnabled = false;
				p.hitArea = new Sprite();
				p.mouseChildren = false;
				
				// Appliquer l'ombre sur les points et la transparence
				p.filters = [_shadow];
				p.alpha = .6;
				
				// On trace un cercle de sa taille
				p.graphics.beginFill(0xFFFFFF, 1);
				p.graphics.drawCircle(0, 0, DEFAULT_TOUCH_SIZE / 2);
				
				// On ajoute les clips au stage et une ref au tableau pour s'en souvenir
				__touchPoints[e.touchPointID] = p;
				DocumentBase.stage.addChildAt(p, Math.max(1, DocumentBase.stage.numChildren - 1));
				
				// On déplace une fois
				touchMoveHandler(e);
			}
		}
		
		/**
		 * Lorsqu'un point bouge
		 * @param	e
		 */
		static protected function touchMoveHandler (e:TouchEvent):void 
		{
			// Si ce point est mémorisé (vérifier avec l'id)
			if (__touchPoints[e.touchPointID] != null)
			{
				// On place le point visuel
				(__touchPoints[e.touchPointID] as Sprite).x = e.stageX;
				(__touchPoints[e.touchPointID] as Sprite).y = e.stageY;
			}
		}
		
		/**
		 * Lorsqu'un point est relâché
		 * @param	e
		 */
		static protected function touchEndHandler (e:TouchEvent):void 
		{
			// Si le point est mémorisé
			if (__touchPoints[e.touchPointID] != null)
			{
				// Masquer
				if (Multitouch.inputMode != MultitouchInputMode.TOUCH_POINT)
					Mouse.show();
				
				// Virer le point du stage et de la liste
				DocumentBase.stage.removeChild((__touchPoints[e.touchPointID] as Sprite));
				__touchPoints[e.touchPointID] = null;
			}
		}
		
		/**
		 * Emuler les touchEvent avec la souris sur un clip
		 * @param	pTarget : Le clip à émuler
		 */
		public static function emulate (pTarget:DisplayObject):void
		{
			// Vérifier si on est pas en mode multiTouch (ça n'aurait aucun sens d'émuler tête de noeud)
			// Vérifier aussi si ce clip n'est pas déjà émulé (pour éviter les doublons et ouai)
			if (Multitouch.inputMode != MultitouchInputMode.TOUCH_POINT && !pTarget.hasEventListener(MouseEvent.MOUSE_DOWN))
			{
				//Debug.core(TouchEmulator, "TOUCH EMULATOR ADDED", arguments);
				
				// Créer un nouvel émulateur
				new TouchEmulator(pTarget);
				
				// Initialiser l'émulation du multiPoints
				emulateMultiTouch();
			}
		}
		
		/**
		 * Créer un nouvel émulateur
		 * @param	pTarget : Le clip à émuler
		 */
		public function TouchEmulator (pTarget:DisplayObject)
		{
			// Mémoriser le clip
			_target = pTarget;
			
			// Ecouter les events de la souris
			pTarget.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			pTarget.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			pTarget.addEventListener(MouseEvent.CLICK, clickHandler);
			pTarget.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			pTarget.addEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
			pTarget.addEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
			
			// Ecouter lorsque le clip est supprimé pour virer les event
			pTarget.addEventListener(Event.REMOVED_FROM_STAGE, dispose);
		}
		
		/**
		 * Le clic est enfoncé
		 * @param	e
		 */
		protected function mouseDownHandler(e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_BEGIN, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
			
			// Si on est en mode multitouch
			if (__pinchMode)
			{
				// Récupérer le point en local
				var localPoint:Point = (e.currentTarget as DisplayObject).globalToLocal(__pinchPoint);
				
				// Créer le faux évènement
				var de2:TouchEvent = new TouchEvent(
					TouchEvent.TOUCH_BEGIN, 
					true, 
					true, 
					SECOND_TOUCH_ID, 
					true, 
					localPoint.x + (localPoint.x - (e.currentTarget as DisplayObject).mouseX), 
					localPoint.y + (localPoint.y - (e.currentTarget as DisplayObject).mouseY), 
					DEFAULT_TOUCH_SIZE, 
					DEFAULT_TOUCH_SIZE, 
					1, 
					null, 
					e.ctrlKey, 
					e.altKey, 
					e.shiftKey
				);
				
				// Dispatcher
				_target.dispatchEvent(de2);
			}
		}
		
		/**
		 * Le clic est relâché
		 * @param	e
		 */
		protected function mouseUpHandler(e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_END, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
			
			// Si on est en mode multitouch
			if (__pinchMode)
			{
				// Récupérer le point en local
				var localPoint:Point = (e.currentTarget as DisplayObject).globalToLocal(__pinchPoint);
				
				// Créer le faux évènement
				var de2:TouchEvent = new TouchEvent(
					TouchEvent.TOUCH_END, 
					true, 
					true, 
					SECOND_TOUCH_ID, 
					true, 
					localPoint.x + (localPoint.x - (e.currentTarget as DisplayObject).mouseX), 
					localPoint.y + (localPoint.y - (e.currentTarget as DisplayObject).mouseY), 
					DEFAULT_TOUCH_SIZE, 
					DEFAULT_TOUCH_SIZE, 
					1, 
					null, 
					e.ctrlKey, 
					e.altKey, 
					e.shiftKey
				);
				
				// Dispatcher
				_target.dispatchEvent(de2);
			}
		}
		
		/**
		 * Clic bref
		 * @param	e
		 */
		protected function clickHandler(e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_TAP, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
		}
		
		/**
		 * Déplacement
		 * @param	e
		 */
		protected function mouseMoveHandler (e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_MOVE, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
			
			// Si on est en mode multitouch
			if (__pinchMode)
			{
				// Récupérer le point en local
				var localPoint:Point = (e.currentTarget as DisplayObject).globalToLocal(__pinchPoint);
				
				// Créer le faux évènement
				var de2:TouchEvent = new TouchEvent(
					TouchEvent.TOUCH_MOVE, 
					true, 
					true, 
					SECOND_TOUCH_ID, 
					true, 
					localPoint.x + (localPoint.x - (e.currentTarget as DisplayObject).mouseX), 
					localPoint.y + (localPoint.y - (e.currentTarget as DisplayObject).mouseY), 
					DEFAULT_TOUCH_SIZE, 
					DEFAULT_TOUCH_SIZE, 
					1, 
					null, 
					e.ctrlKey, 
					e.altKey, 
					e.shiftKey
				);
				
				// Dispatcher
				_target.dispatchEvent(de2);
			}
		}
		
		/**
		 * Passer au dessus
		 * @param	e
		 */
		protected function mouseOverHandler (e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_OVER, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
		}
		
		/**
		 * Passer en dehors
		 * @param	e
		 */
		protected function mouseOutHandler (e:MouseEvent):void 
		{
			// Créer le faux évènement
			var de:TouchEvent = new TouchEvent(
				TouchEvent.TOUCH_OUT, 
				true, 
				true, 
				FIRST_TOUCH_ID, 
				true, 
				(e.currentTarget as DisplayObject).mouseX, 
				(e.currentTarget as DisplayObject).mouseY, 
				DEFAULT_TOUCH_SIZE, 
				DEFAULT_TOUCH_SIZE, 
				1, 
				null, 
				e.ctrlKey, 
				e.altKey, 
				e.shiftKey
			);
			
			// Dispatcher
			_target.dispatchEvent(de);
		}
		
		/**
		 * Le clip à été supprimé, on dégage les events de la souris
		 * @param	e
		 */
		protected function dispose(e:Event):void 
		{
			_target.removeEventListener(Event.REMOVED_FROM_STAGE, dispose);
			_target.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			_target.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			_target.removeEventListener(MouseEvent.CLICK, clickHandler);
			_target.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			_target.removeEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
			_target.removeEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
		}
	}
}